/*
 * Copyright (C) 2012-2013  Romain DUBOIS
 * 
 * This file is part of NeMoS - Network and Modular Software
 * 
 * NeMoS is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * NeMoS is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with NeMoS.  If not, see <http://www.gnu.org/licenses/>.
 */
package nemos.remote.util;

import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.UUID;

import nemos.remote.internal.RemoteMessage;
import nemos.util.log.NemosLogger;
import nemos.util.osgi.FilterBuilder;

import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.FrameworkUtil;
import org.osgi.service.remoteserviceadmin.RemoteConstants;

/**
 * RemoteUtil : The remote utility class.
 * 
 * @author Romain DUBOIS
 */
public final class RemoteUtil {

    /** The logger */
    private static final NemosLogger LOGGER = new NemosLogger(RemoteUtil.class);

    /**
     * Get the local framework UUID.
     * 
     * @return The local framework UUID
     */
    public static String getFrameworkUuid() {
        final BundleContext lContext = FrameworkUtil.getBundle(RemoteUtil.class).getBundleContext();

        // Ensure Framework UUID property is set
        // Cf. OSGi compendium §122.4.4
        synchronized (Constants.FRAMEWORK_UUID) {
            String lUuid = lContext.getProperty(Constants.FRAMEWORK_UUID);
            if (lUuid == null) {
                lUuid = UUID.randomUUID().toString();
                System.setProperty(Constants.FRAMEWORK_UUID, lUuid);

                final NemosLogger lLogger = new NemosLogger(RemoteUtil.class);
                lLogger.info(RemoteMessage.FRAMEWORK_UUID_GENERATED, lUuid);
            }

            return lUuid;
        }
    }

    /**
     * Get the filter for local endpoints.
     * 
     * @return The filter for local endpoints
     */
    public static String getLocalEndpointFilter() {
        return FilterBuilder.getStringFilter(RemoteConstants.ENDPOINT_FRAMEWORK_UUID,
                getFrameworkUuid());
    }

    /**
     * Get the filter for remote endpoints.
     * 
     * @return The filter for remote endpoints
     */
    public static String getRemoteEndpointFilter() {
        return FilterBuilder.getStringFilter(RemoteConstants.ENDPOINT_FRAMEWORK_UUID,
                getFrameworkUuid(), true);
    }

    /**
     * Get the filter for all endpoints, excluding those from the specified
     * framework.
     * 
     * @param pFrameworkUuid
     *            The framework to exclude
     * @return The filter string which exclude only endpoints from the framework
     */
    public static String getExcludingEndpointFilter(final String pFrameworkUuid) {
        return FilterBuilder.getStringFilter(RemoteConstants.ENDPOINT_FRAMEWORK_UUID,
                pFrameworkUuid, true);
    }

    /**
     * Try to find the local host name.
     * 
     * @return The local host name
     */
    public static final String findHostName() {
        return findHostName("localhost"); //$NON-NLS-1$
    }

    /**
     * Try to find the local host name.
     * 
     * @param pDefault
     *            The default host name if not found
     * @return The local host name
     */
    public static final String findHostName(final String pDefault) {
        String lHostName = null;

        // Collect all network interfaces
        try {
            final Enumeration<NetworkInterface> lIntfs = NetworkInterface.getNetworkInterfaces();
            while (lIntfs.hasMoreElements() && lHostName == null) {
                final NetworkInterface lIntf = lIntfs.nextElement();

                // Then all addresses of the network interface
                final Enumeration<InetAddress> lAddresses = lIntf.getInetAddresses();
                while (lAddresses.hasMoreElements() && lHostName == null) {
                    final InetAddress lAddress = lAddresses.nextElement();
                    final String lName = lAddress.getHostAddress();

                    if (!lName.startsWith("localhost") && !lName.startsWith("127.") && !lName.equals("0:0:0:0:0:0:0:1")) { //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
                        lHostName = lName;
                    }
                }
            }
        } catch (final SocketException e) {
            LOGGER.warn(RemoteMessage.UNABLE_TO_GET_HOST_NAME, e);
        }

        // Does not work with all configurations...
        if (lHostName == null) {
            try {
                lHostName = InetAddress.getLocalHost().getCanonicalHostName();
            } catch (final UnknownHostException e) {
                LOGGER.warn(RemoteMessage.UNABLE_TO_GET_HOST_NAME, e);
            }
        }

        // Unable to find the host name
        if (lHostName == null) {
            lHostName = pDefault;
        }

        return lHostName;
    }

    /**
     * Constructor (private for utility class)
     */
    private RemoteUtil() {
        super();
    }

}
